import React, { useCallback, useEffect, useState, useMemo, useRef, useImperativeHandle, forwardRef } from 'react';
import { Select as AntdSelect, Tooltip, Input } from 'antd';
import { SelectProps as AntdSelectProps } from 'antd/lib/select';
import { valueType } from 'antd/lib/statistic/utils';
import { SelectProps } from './interface';
import { getData } from './Api';
import { handleChange, handleValue, evalJsStr, setInitExtra } from './util';
import { parse } from './../../utils/json/json';
import QueryProps from '@library/interface/apiQuery';
import Icon from './../icon/Icon';
import style from './select.module.scss';

const { Option } = AntdSelect;

type NewSelectProps = SelectProps & AntdSelectProps<valueType>;

const Select = (props: NewSelectProps, ref: React.Ref<any>) => {
  const isInitRef = useRef(false);
  const lastQuery = useRef<any>(null);
  const searchTimeout = useRef<any>(null);
  const [data, setData] = useState<Record<string, any>[]>([]);
  const [showSelect, setShowSelect] = useState(false);
  const [loading, setLoading] = useState(false);

  const { fieldNames: propFieldNames, cacheKey, id, extra, onChange, url, query, isTooltip, ...selectProps } = props;

  useImperativeHandle(ref, () => ({
    clearDataSource() {
      setData([]);
    }
  }));

  const fieldNames = useMemo(() => {
    return propFieldNames || { label: 'label', value: 'value' };
  }, [propFieldNames]);

  const initReq = async () => {
    let _query = props.handleQuery?.(query || {}) || query || {};
    getData(url!, _query, cacheKey, props.rtnDataAfter, (rows: any[]) => {
      let temp: Record<string, any> = {};

      for (let i of rows) {
        const value = evalJsStr(i, fieldNames.value);
        temp[value] = i;
      }

      const data = Object.values(temp);

      setData(data);
      if (id) setInitExtra(id, props.value as string, data, fieldNames);
    })
  };

  useEffect(() => {
    if (props.value && url && !isInitRef.current) {
      initReq();
      isInitRef.current = true;
    }
  }, [url, props.value]);

  useEffect(() => {
    if (lastQuery.current && lastQuery.current !== query) {
      if (JSON.stringify(query) !== JSON.stringify(lastQuery.current)) setData([]);
    }
    lastQuery.current = query;
  }, [query]);

  const dataSource = useMemo(() => {
    if (props.dataSource) {
      return typeof props.dataSource === 'string' ? parse(props.dataSource) : props.dataSource;
    }
    return data;
  }, [props.dataSource, data]);

  const _onChange = useCallback(
    (value) => {
      handleChange({ value, id, dataSource, fieldNames, extra, onChange });
    },
    [onChange, dataSource, fieldNames, id, extra]
  );

  const onSearch =
    (value: string) => {
      if (searchTimeout.current) {
        clearTimeout(searchTimeout.current);
        searchTimeout.current = null;
      }

      if (!value) return setData([]);

      searchTimeout.current = setTimeout(async () => {
        const searchQuery: QueryProps = {
          ...query,
          pages: { page: 1, pageSize: 20 },
          fuzzyValue: value,
          fuzzyQuery: true,
          fuzzyField: [fieldNames.value, fieldNames.label],
        };

        searchTimeout.current = null;

        setLoading(true);
        getData(url!, props.handleQuery?.(searchQuery) || searchQuery, undefined, props.rtnDataAfter, (rows: any[]) => {
          setLoading(false);
          setData(rows);
        });
      }, 500);
    };

  const _onFocus = useCallback(
    (event) => {
      props?.onFocus?.(event);
      if (data.length || !url) return;
      setLoading(true);
      initReq();
      isInitRef.current = true;
      setLoading(false);
    },
    [data, initReq, url, query, props.onFocus]
  );

  const value = useMemo(() => {
    let _value = handleValue(props.value, props.mode);

    if (_value && !Array.isArray(_value) && props.fieldNames) {
      if (!dataSource.find((item: Record<string, any>) => item[props.fieldNames.value] === _value)) {
        return props.oValue?.[props.fieldNames.label];
      }
    }

    return _value;
  }, [props.value, props.oValue, props.mode, dataSource, props.fieldNames]);

  const disabledShowValue = useMemo(() => {
    const valueKey = fieldNames.value;
    if (dataSource?.length && valueKey) {
      const item = dataSource.find((item: any) => item[valueKey] === value);
      if (item) {
        return evalJsStr(item, fieldNames.label);
      }
    }

    return value;
  }, [value, dataSource, fieldNames.value, fieldNames.label]);

  return props.disabled ? (
    <Input
      value={(disabledShowValue ?? null) as any}
      disabled
      style={selectProps.style}
      className={selectProps.className}
    />
  ) : (
    <AntdSelect
      suffixIcon={
        showSelect && selectProps.showSearch ? (
          <Icon className={style.searchIcon} icon="search" />
        ) : (
          <Icon className={style.searchIcon} icon="&#xe631;" />
        )
      }
      onDropdownVisibleChange={(e) => setShowSelect(e)}
      {...selectProps}
      showSearch={selectProps.showSearch}
      {...(selectProps.showSearch ? { onSearch: onSearch } : null)}
      filterOption={false}
      allowClear={!selectProps.required}
      value={(value ?? null) as any}
      {...(!selectProps.showSearch ? { onFocus: _onFocus } : null)}
      loading={loading}
      placeholder={props?.placeholder}
      onChange={_onChange}
    >
      {dataSource.map((item: any) => {
        const label = evalJsStr(item, fieldNames.label);
        return (
          <Option key={item.id || item.value} value={evalJsStr(item, fieldNames.value)}>
            {isTooltip ? (
              <Tooltip title={label}>
                <div>{label}</div>
              </Tooltip>
            ) : (
              label
            )}
          </Option>
        );
      })}
    </AntdSelect>
  );
};

export default forwardRef(Select);
